package plp.orientadaObjetos2.declaracao.funcao;

import plp.expressions2.memory.VariavelJaDeclaradaException;
import plp.expressions2.memory.VariavelNaoDeclaradaException;
import plp.orientadaObjetos1.comando.Comando;
import plp.orientadaObjetos1.declaracao.procedimento.ListaDeclaracaoParametro;
import plp.orientadaObjetos1.excecao.declaracao.ClasseJaDeclaradaException;
import plp.orientadaObjetos1.excecao.declaracao.ClasseNaoDeclaradaException;
import plp.orientadaObjetos1.excecao.declaracao.ProcedimentoJaDeclaradoException;
import plp.orientadaObjetos1.excecao.declaracao.ProcedimentoNaoDeclaradoException;
import plp.orientadaObjetos1.expressao.Expressao;
import plp.orientadaObjetos1.expressao.leftExpression.Id;
import plp.orientadaObjetos1.memoria.AmbienteCompilacaoOO1;
import plp.orientadaObjetos1.util.Tipo;
import plp.orientadaObjetos2.memoria.AmbienteCompilacaoOO2;
import plp.orientadaObjetos2.memoria.FuncaoJaDeclaradaException;

public class DecFuncaoConcreta implements DecFuncao {

	/**
	 * Identificador do tipo de retorn desta funcao.
	 */
	protected Tipo tipoRetorno;
	
	/**
     * Identificador do funcao.
     */
    protected Id nome;

    /**
     * Parametros declarados para esta funcao.
     */
    protected ListaDeclaracaoParametro parametrosFormais;

    /**
     * Comando representando o corpo da funcao.
     */
    protected Comando comando;

    /**
     * Expressao que sera avaliada e cujo valor sera retornado pelo pseudo-comando de retorno
     */
    protected Expressao expressaoRetorno;

	public DecFuncaoConcreta(Tipo tipoRetorno, Id nome,
			ListaDeclaracaoParametro parametrosFormais, Comando comando,
			Expressao expressaoRetorno) {
		super();
		this.tipoRetorno = tipoRetorno;
		this.nome = nome;
		this.parametrosFormais = parametrosFormais;
		this.comando = comando;
		this.expressaoRetorno = expressaoRetorno;
	}

	public boolean checaTipo(AmbienteCompilacaoOO1 ambiente)
			throws VariavelJaDeclaradaException, VariavelNaoDeclaradaException,
			ProcedimentoJaDeclaradoException,
			ProcedimentoNaoDeclaradoException, ClasseJaDeclaradaException,
			ClasseNaoDeclaradaException, FuncaoJaDeclaradaException {
		boolean tipoOk = false;
		if(this.parametrosFormais.checaTipo(ambiente)) {
			((AmbienteCompilacaoOO2)ambiente).mapFuncao(nome, this);
            ambiente.incrementa();
            ambiente = parametrosFormais.declaraParametro(ambiente);
            if(this.comando != null){
            	tipoOk = comando.checaTipo(ambiente);
            } else {
            	tipoOk = true;
            }
            tipoOk = tipoOk &&
            			this.expressaoRetorno.checaTipo(ambiente) &&
            			this.expressaoRetorno.getTipo(ambiente).equals(this.tipoRetorno);
            ambiente.restaura();
        }
        return tipoOk;
	}

	public Tipo getTipoRetorno() {
		return tipoRetorno;
	}

	public Id getNome() {
		return nome;
	}

	public ListaDeclaracaoParametro getParametrosFormais() {
		return parametrosFormais;
	}

	public Comando getComando() {
		return comando;
	}

	public Expressao getExpressaoRetorno() {
		return expressaoRetorno;
	}

}
